/*
 * Team Name: Team Sasquatch
 * Cody Sharff
 * Anthony Rios
 * Prince Kishore
 * Travis Drake
 */

package gui;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.Observer;
import java.util.Scanner;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Pattern;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import model.Auction;
import model.Condition;
import model.ErrorType;
import model.Item;
import model.MasterCollection;
import model.SortType;
import model.User;
import model.UserType;



/**
 * The master class governing our User Interface.
 * @author Travis Drake
 * @author Modified by Anthony Rios
 * @author Modified by Cody Sharff
 * @version May 18, 2012
 */
public class MasterGUI implements Observer
{
    private final static String 
            MASTER_COLLECTION_TEXT = "MASTERCOLLECTION";
    private final static String 
            STATIC_TEXT = "STATIC"; 
    private final static String 
            STATIC_NEXT_AUCTION_TEXT = "NEXTAUCTION";   
    private final static String 
            AUCTION_TEXT = "AUCTION";     
    private final static String 
            AUCTION_ID_TEXT = "ID";
    private final static String 
            AUCTION_NPO_TEXT = "NPO";
    private final static String 
            AUCTION_CONTACT_PERSON_TEXT = "CONTACTNAME";
    private final static String 
            AUCTION_CONTACT_PERSON_NUMBER_TEXT = "CONTACTNUMBER";
    private final static String 
            AUCTION_INTAKE_PERSON_TEXT = "INTAKENAME";
    private final static String 
            AUCTION_DATE_TEXT = "DATE";
    private final static String 
            AUCTION_DURATION_TEXT = "DURATION";
    private final static String 
            AUCTION_COMMENT_TEXT = "COMMENT";
    private final static String 
            ITEM_TEXT = "ITEM";   
    private final static String 
            STATIC_NEXT_ITEM_TEXT = "NEXTITEM";   
    private final static String 
            ITEM_ID_TEXT = "ID";
    private final static String 
            ITEM_NAME_TEXT = "NAME";
    private final static String 
            ITEM_QUANTITY_TEXT = "QUANTITY";
    private final static String 
            ITEM_MIN_BID_TEXT = "MINBID";
    private final static String 
            ITEM_DONOR_TEXT = "DONOR";
    private final static String 
            ITEM_SIZE_TEXT = "SIZE";
    private final static String 
            ITEM_STORAGE_TEXT = "STORAGE";
    private final static String 
            ITEM_CONDITION_TEXT = "CONDITION";
    private final static String 
            ITEM_BIDS_TEXT = "BIDS";
    private final static String 
            BID_TEXT = "BID";
    private final static String 
            ITEM_COMMENT_TEXT = "COMMENT";
    private final static String 
            USER_TEXT = "USER";   
    private final static String 
            USER_NAME_TEXT = "NAME";
    private final static String 
            USER_TYPE_TEXT = "TYPE";
    private final static String 
            USER_PASS_TEXT = "PASS";
    private final static String
            L_WICKET = "<";
    private final static String
            L_SLASH_WICKET = "</";
    private final static String
            R_WICKET = ">";
    private final static String
            NEWLINE = "\n";
    private final static String
            TABCHAR = "\t";
    private final static String
            SPACE = " ";
    

    /**
     * A regex pattern that will return any tag matching the <????>
     * format that our database is using.
     */
    private final static Pattern ANY_TAG = Pattern.compile("<{1}[^<]*>{1}", Pattern.CASE_INSENSITIVE);
    
    private StartUpGUI my_start_up_gui;
    
    /**
     * The MasterCollection for our
     */
    private MasterCollection my_master_collection;
    
    /**
     * the frame used to display all the panels for this project.
     */
    private MainFrame my_main_frame;
    
    private CalendarGUI my_calendar;
    
    private AuctionViewGUI my_auction_view_gui;
    
    private AddAuctionGUI my_add_auction_gui;
    
    private AddItemGUI my_add_item_gui;
    
    private AuctionListGUI my_auction_list_gui;
    
    private ItemViewGUI my_item_view_gui;
    
    private ViewBidsGUI my_view_bids_gui;
    
    private File my_database;
    
    /**
     * a flag that is not zero when a auction has been added and 0 when one has
     * not been added.
     */
    private int my_auction_added_flag = 0;
    
    /**
     * a flag to check if add auction was selected from the calendar or the auction list.
     */
    private int my_calendar_add_auction_flag = 0;
    
    /**
     * This field is to prevent a double reference between item and auction.
     * The way the add item works in master collection is it needs an auction
     * id to add the item.  Since we are using observable i could not send back the 
     * item and auction at the same time so instead i sent them back separately
     * and i store the auction here temporarily.
     */
    private Auction my_temp_auction;
    
    /**
     * used to go back to a previous panel.
     */
    private Stack<JPanel> my_panel_stack = new Stack<JPanel>();
    
    /**
     * Null until given a valid login by the StartUpGUI.
     */
    private User my_logged_in = null;
    
    
    
    /**
     * A static reference to the masterGUI that we generate in
     * main so that static methods can get access to it. Not the 
     * best practice but it makes working in a half static - half
     * instantiated class much easier.
     */
    private static MasterGUI my_master_gui;
    
    /**
     * The private constructor for our MasterGUI. Doesnt need to be
     * public at the moment, so no reason to cause any security risks.
     * @Author Travis Drake
     * @param the_file
     * 
     * @requires the_file XML file format
     * @ensures MasterGUI
     */
    public MasterGUI(File the_file) {
        my_master_collection = new MasterCollection();
        
        try
        {
            parseDatabase(the_file);           
            //This shouldnt be necessary since we  
            //thoroughly ensured the file was there
            //but must warn the user just in case.
        } catch (FileNotFoundException e)      
        {                                      
            System.out.println("SOMETHING TERRIBLE HAPPENED, " +
        "FileNotFoundException when we guaranteed the file was readable.");
            System.exit(1);
            
        } catch (IncorrectlyFormattedDatabaseException e)
        {
            System.out.println("Bad database file, not properly formatted.");
            System.exit(2);
        }

        my_start_up_gui = new StartUpGUI();
        my_main_frame = new MainFrame(this);
        my_main_frame.setLocationRelativeTo(null);
        my_database = the_file;

    }

    /**
     * this is the main for the program.
     */
    public static void main(String[] args) {
        my_master_gui = new MasterGUI(new File("src/database/DatabaseInOut.txt"));
        my_master_gui.my_start_up_gui.addObserver(my_master_gui);
        my_master_gui.my_start_up_gui.start();
    }
    
   /**
    * This method is called when it is detected that a Sub GUI has
    * changed.
    * 
    * @author Anthony Rios
    */
    @Override
    public void update(Observable the_updated, Object the_obj)
    {
      if (the_updated instanceof StartUpGUI)
      {
    	  startUpGUIUpdate();
      }
      else if (the_updated instanceof CalendarGUI) {
    	  calendarGuiUpdate(the_updated, the_obj);
      }
      else if (the_updated instanceof AddItemGUI) {
    	  addItemGuiUpdate(the_updated, the_obj);
      }
      else if (the_updated instanceof AuctionListGUI) {
    	  auctionListGuiUpdate(the_updated, the_obj);
      }
      else if (the_updated instanceof AuctionViewGUI) {
    	  auctionViewGuiUpdate(the_updated, the_obj);
      }
      else if (the_updated instanceof ItemViewGUI) {
    	  itemViewGuiUpdate(the_updated, the_obj);
      }
      else if (the_updated instanceof AddAuctionGUI) {
    	  addAuctionGuiUpdate(the_updated, the_obj);
      }
      else {
    	  cancelAndBackButton();
      }
    }

   /**
    * Helper method for the update method.  This method decides what to do
    * with the different kinds of changes that occur within startUpGUI.
    * 
    * @author Anthony Rios
    */
   private void startUpGUIUpdate() {
	   if (my_master_collection.containsUser(my_start_up_gui.getLogin()))
       {

           my_logged_in = my_master_collection.getUser(my_start_up_gui.getLogin());
           if (my_logged_in.checkPassword(my_start_up_gui.getPassword()))
           {
        	  newCalendar();
         	  my_main_frame.start();
         	  my_start_up_gui.dispose(); 
           }
           else
           {
               my_start_up_gui.displayError();
           }
       }
       else
       {
           my_start_up_gui.displayError();
       }
   }
   /**
    * Helper method for the update method.  This method decides what to do
    * with the different kinds of changes that occur within calendarGUI.
    * 
    * @author Anthony Rios
    */
   private void calendarGuiUpdate(Observable the_updated, Object the_obj) 
   {
       if (the_obj == null) {
           my_calendar.refresh();
           my_main_frame.pack();
           my_main_frame.repaint();
       } else if (the_obj.equals(CalendarGUI.VIEW_BUTTON)) 
       {
		   my_auction_list_gui = new AuctionListGUI(my_master_collection.getAuctions(SortType.DATE), my_logged_in);
		   my_auction_list_gui.addObserver(my_master_gui);
		   my_panel_stack.push(((CalendarGUI) the_updated).getPanel());
		   my_main_frame.setContentPane(my_auction_list_gui.getPanel());
		   my_main_frame.setTitle(my_auction_list_gui.getPanel().getName());
		   my_main_frame.pack();
		   my_main_frame.repaint();
    	}
	   else if (the_obj instanceof Calendar) {
		   my_calendar_add_auction_flag = 1;
		   my_add_auction_gui = new AddAuctionGUI(my_logged_in, (Calendar) the_obj);
		   my_add_auction_gui.addObserver(my_master_gui);
		   my_panel_stack.push(((CalendarGUI) the_updated).getPanel());
		   my_main_frame.setContentPane(my_add_auction_gui.getPanel());
		   my_main_frame.setTitle(my_add_auction_gui.getPanel().getName());
		   my_main_frame.pack();
		   my_main_frame.repaint();
	   }
	   else if (the_obj instanceof Auction)
	   {
           my_auction_view_gui = new AuctionViewGUI((Auction) the_obj, my_logged_in);
           my_auction_view_gui.addObserver(my_master_gui);
           my_panel_stack.push(((CalendarGUI) the_updated).getPanel());
           my_main_frame.setContentPane(my_auction_view_gui.getPanel());
           my_main_frame.setTitle(my_auction_view_gui.getPanel().getName());
           my_main_frame.pack();
           my_main_frame.repaint();
	   }
	   else {
		   List<String> a_item_list = createItemList();
		   List<Integer> a_bids_list = createItemBidAmountList();
		   my_view_bids_gui = new ViewBidsGUI(a_item_list,a_bids_list);
		   my_view_bids_gui.addObserver(my_master_gui);
           my_panel_stack.push(((CalendarGUI) the_updated).getPanel());
           my_main_frame.setContentPane(my_view_bids_gui.getPanel());
           my_main_frame.setTitle(my_view_bids_gui.getPanel().getName());
           my_main_frame.pack();
           my_main_frame.repaint();
	   }
   }
   
   /**
    * Helper method for the update method.  This method decides what to do
    * with the different kinds of changes that occur within addItemGUI.
    * 
    * @author Anthony Rios
    */
   private void addItemGuiUpdate(Observable the_updated, Object the_obj) {
	   if (the_obj instanceof Auction) {
		   my_temp_auction = (Auction) the_obj;
	   }
	   else if (the_obj instanceof Item) {
		   my_master_collection.addAuctionItem
		   		(my_temp_auction.getAuctionID(), (Item) the_obj);
		   my_panel_stack.pop();
		   my_auction_view_gui = new AuctionViewGUI(my_temp_auction, my_logged_in);
		   my_auction_view_gui.addObserver(my_master_gui);
		   my_main_frame.setContentPane(my_auction_view_gui.getPanel());
		   my_main_frame.setTitle(my_auction_view_gui.getPanel().getName());
		   my_main_frame.pack();
		   my_main_frame.repaint();
 	  } else if (the_obj.equals(AddAuctionGUI.CANCEL)) {
		   cancelAndBackButton();
	   }
   }
   
   /**
    * Helper method for the update method.  This method decides what to do
    * with the different kinds of changes that occur within auctionListGUI.
    * 
    * @author Anthony Rios
    */
   private void auctionListGuiUpdate(Observable the_updated, Object the_obj) {
	   if (the_obj instanceof Auction) {
 		  	my_auction_view_gui = new AuctionViewGUI((Auction) the_obj, my_logged_in);
 		  	my_auction_view_gui.addObserver(my_master_gui);
 		  	my_panel_stack.push(((AuctionListGUI) the_updated).getPanel());
 		  	my_main_frame.setContentPane(my_auction_view_gui.getPanel());
 		  	my_main_frame.setTitle(my_auction_view_gui.getPanel().getName());
 		  	my_main_frame.pack();
 		  	my_main_frame.repaint();
	   } 
	   else if (the_obj.equals(ItemViewGUI.BACK)) {
		   if (my_auction_added_flag == 0) {
			    cancelAndBackButton();
		   }
		   else {
		    	my_panel_stack.pop();
		    	newCalendar();
		    	my_auction_added_flag = 0;
		   }
 	   } 
	   else if (the_obj.equals(AuctionViewGUI.ADD_AUCTION)) {
 		   	my_add_auction_gui = new AddAuctionGUI(my_logged_in);
 		   	my_add_auction_gui.addObserver(my_master_gui);
 		  	my_panel_stack.push(((AuctionListGUI) the_updated).getPanel());
		  	my_main_frame.setContentPane(my_add_auction_gui.getPanel());
		  	my_main_frame.setTitle(my_add_auction_gui.getPanel().getName());
		  	my_main_frame.pack();
		  	my_main_frame.repaint();
 	   }
   }
   
   /**
    * Helper method for the update method.  This method decides what to do
    * with the different kinds of changes that occur within auctionViewGUI.
    * 
    * @author Anthony Rios
    */
   private void auctionViewGuiUpdate(Observable the_updated, Object the_obj) {
	   if (the_obj instanceof Item) {
 		  	my_item_view_gui = new ItemViewGUI((Item) the_obj, my_logged_in);
 		  	my_item_view_gui.addObserver(my_master_gui);
 		  	my_panel_stack.push(((AuctionViewGUI) the_updated).getPanel());
 		  	my_main_frame.setContentPane(my_item_view_gui.getPanel());
 		  	my_main_frame.setTitle(my_item_view_gui.getPanel().getName());
 		  	my_main_frame.pack();
 		  	my_main_frame.repaint();
 	  	} else if (the_obj.equals(ItemViewGUI.BACK)) {
 	  		cancelAndBackButton();
 	  	} else if (the_obj instanceof Auction) {
 	  		my_add_item_gui = new AddItemGUI((Auction) the_obj);
 	  		my_add_item_gui.addObserver(my_master_gui);
 		  	my_panel_stack.push(((AuctionViewGUI) the_updated).getPanel());
 		  	my_main_frame.setContentPane(my_add_item_gui.getPanel());
 		  	my_main_frame.setTitle(my_add_item_gui.getPanel().getName());
 		  	my_main_frame.pack();
 		  	my_main_frame.repaint();
 	  	}
   }
   
   /**
    * Helper method for the update method.  This method decides what to do
    * with the different kinds of changes that occur within itemViewGUI.
    * 
    * @author Anthony Rios
    */
    private void itemViewGuiUpdate(Observable the_updated, Object the_obj) {
   	    if (the_obj.equals(ItemViewGUI.BACK)) {
   	    	cancelAndBackButton();
   	    }
	}
    
    /**
     * helper method for the update method.  This method decides what to do
     * with the different kinds of changes that occur within addAuctionGUI.
     * @author Anthony Rios
     * @author Modified by Cody Sharff
     * 		   Added error checking before auction was added.
     */
    private void addAuctionGuiUpdate(Observable the_updated, Object the_obj) {
    	if (the_obj instanceof Auction) {
    		List<ErrorType> error = my_master_collection.checkValid((Auction) the_obj);
    		if (error.size() > 0 && (error.get(0) != ErrorType.NO_ERROR)) {
    			displayError(error);
    		} else {
    			if (my_calendar_add_auction_flag == 0) { //if auction was added from the auction list
    				my_master_collection.addAuction((Auction) the_obj);
    				my_panel_stack.pop();
    				my_auction_list_gui = new AuctionListGUI(my_master_collection.getAuctions(SortType.DATE), my_logged_in);
    				my_auction_added_flag = 1;
    				my_auction_list_gui.addObserver(my_master_gui);
    				my_main_frame.setContentPane(my_auction_list_gui.getPanel());
    				my_main_frame.setTitle((my_auction_list_gui.getPanel().getName()));
    				my_main_frame.pack();
    				my_main_frame.repaint();
    			} else { //auction was added from the calendar
    				my_master_collection.addAuction((Auction) the_obj);
    				my_main_frame.setContentPane(my_panel_stack.pop());
    				my_main_frame.setTitle(my_calendar.getPanel().getName());
    				my_calendar.refresh();
    				my_auction_added_flag = 1;
    				my_calendar_add_auction_flag = 0;
    				my_main_frame.pack();
    				my_main_frame.repaint();
    			}
    		}
    	} else if (the_obj.equals(AddAuctionGUI.CANCEL)) {
    		cancelAndBackButton();
    	}
    }

    /**
     * Checks the error received when trying to add an auction and prints a
     * relevant message to the user.
     * 
     * @author Cody Sharff
     */
    private void displayError(List<ErrorType> error) {
    	switch (error.get(0)) {
    		case MORE_THAN_25_AUCTIONS:
    			JOptionPane.showMessageDialog(null, "Try again later, maximum amount of auctions already scheduled!", "Error", JOptionPane.ERROR_MESSAGE);
    			break;
    		case MORE_THAN_5_AUCTIONS_IN_7_DAYS:
    			JOptionPane.showMessageDialog(null, "Please choose another date, too many auctions in a 7 day period!", "Error", JOptionPane.ERROR_MESSAGE);
    			break;
    		case TOO_SOON_TO_END_OF_PREVIOUS_AUCTION:
    			JOptionPane.showMessageDialog(null, "Too close to the end of the previous auction!", "Error", JOptionPane.ERROR_MESSAGE);
    			break;
    		case FARTHER_THAN_3_MONTHS:
    			JOptionPane.showMessageDialog(null, "Please schedule auction within three months of current date!", "Error", JOptionPane.ERROR_MESSAGE);
    			break;
    		case MORE_THAN_2_AUCTIONS_IN_A_DAY:
    			JOptionPane.showMessageDialog(null, "Please schedule auction within three months of current date!", "Error", JOptionPane.ERROR_MESSAGE);
    			break;
    		case MORE_THAN_ONE_AUCTION_PER_YEAR:
    			JOptionPane.showMessageDialog(null, "Your non profit organization can only have one auction per year!", "Error", JOptionPane.ERROR_MESSAGE);
    			break;
    		case BEFORE_CURRENT_DATE:
    			JOptionPane.showMessageDialog(null, "The auction date has already passed!", "Error", JOptionPane.ERROR_MESSAGE);
    			break;
    		default:
    			JOptionPane.showMessageDialog(null, "Uncategorized error!", "Error", JOptionPane.ERROR_MESSAGE);
    			break;
		}
	}
    
    /**
     * Helper method for ViewBidderGui. This method create a list
     * containing all the item the bidder has bid on.  If the for loops in this
     * method are changed the exact iteration must be 
     * changed in createItemBidAmountList() also.
     * 
     * @author Anthony Rios
     */
    private List<String> createItemList() {
    	List<String> a_list = new ArrayList<String>();
    	System.out.println(my_calendar.getActualYear());
    	System.out.println(my_calendar.getActualMonth() + 1);
    	System.out.println(my_calendar.getActualDay());
    	for (int i = 0; i < my_master_collection.getAuctions().size(); i++) {
    		Auction a_auction = my_master_collection.getAuctions().get(i);
    		for (int j = 0; j < a_auction.getItems().size(); j++) {
    			Item a_item = a_auction.getItems().get(j);
    			if (my_calendar.getActualYear() <= a_auction.getDate().get(Calendar.YEAR) &&
    					(my_calendar.getActualMonth() + 1) <= a_auction.getDate().get(Calendar.MONTH)	&&
    					/*my_calendar.getActualDay() <= a_auction.getDate().get(Calendar.DAY_OF_MONTH)
    					&&*/ a_item.getBids().containsKey(my_logged_in.my_ID)) {
    				String a_string = a_item.toString() + "/" + a_item.getItemID();
    				a_list.add(a_string);
    			}
    		}
    	}
    	return a_list;
    }
    
    /**
     * Helper method for ViewBidderGui. This method create a list
     * containing all the bid amounts the bidder placed on items.  If the for loops in
     * this method are changed the exact iteration must be changed
     * in the createItemList() method also.
     * 
     * @author Anthony Rios
     */
    private List<Integer> createItemBidAmountList() {
    	List<Integer> a_list = new ArrayList<Integer>();
    	for (int i = 0; i < my_master_collection.getAuctions().size(); i++) {
    		Auction a_auction = my_master_collection.getAuctions().get(i);
    		for (int j = 0; j < a_auction.getItems().size(); j++) {
    			Item a_item = a_auction.getItems().get(j);
    			if (my_calendar.getActualYear() <= a_auction.getDate().get(Calendar.YEAR) &&
    					(my_calendar.getActualMonth() + 1) <= a_auction.getDate().get(Calendar.MONTH) &&
    					/*my_calendar.getActualDay() <= a_auction.getDate().get(Calendar.DAY_OF_MONTH)
    					&&*/ a_item.getBids().containsKey(my_logged_in.my_ID)) {
    				a_list.add(a_item.getAPlacedBid(my_logged_in.my_ID));
    			}
    		}
    	}
    	return a_list;
    }

	/**
     * Helper method goes back to the previous panel after the back or cancel
     * button is selected.
     * 
     * @author Anthony Rios
     */
    private void cancelAndBackButton() {
    	  JPanel temp_panel = my_panel_stack.pop();
		  my_main_frame.setContentPane(temp_panel);
		  my_main_frame.setTitle(temp_panel.getName());
		  my_main_frame.pack();
		  my_main_frame.repaint();
    }
    
    /**
     * Helper method puts a new calendar with the 
     * updated auction info into the main frame.
     * 
     * @author Anthony Rios
     */
    private void newCalendar() {
    	  my_calendar = new CalendarGUI(my_logged_in, my_master_collection);
		  my_calendar.addObserver(my_master_gui);
		  my_main_frame.setContentPane(my_calendar.getPanel());
		  my_main_frame.setTitle(my_calendar.getPanel().getName());
		  my_main_frame.pack();
		  my_main_frame.repaint();
    }


    
    /**
     * A helper method to loop through lines in the scanner 
     * looking for the next tag, skipping lines till it reaches
     * the next one.
     * @Author Travis Drake
     * 
     * @param the_scanner   The scanner this method is helping to parse.
     * @param the_match     The last matched tag in the scanner.
     * @return              The next tag, as updated by this helper method.
     */
    private String parseHelper(Scanner the_scanner, String the_match)
    {
        the_match = the_scanner.findInLine(ANY_TAG);
        while (the_match == null)
        {
               the_scanner.nextLine();
               the_match = the_scanner.findInLine(ANY_TAG);
        }

        return the_match;
    }


   /**
    * @author Travis Drake
    * Regex refreshed at various sites, but formulated this
    * all myself, including tag matching. It was
    * a rudimentary system and could be built a little
    * better (although all the constant Strings are
    * pretty necessary because theres no way to use
    * human readable tags without them.
    * 
    * @throws FileNotFoundException //should never be thrown because
    * we ensure the file is readable.
    * @throws IncorrectlyFormattedDatabaseException 
    * 
    * @requires the_database proper formatting
    * @ensures Proper parsing
    * 
    */
    private void parseDatabase(File the_database) throws FileNotFoundException,
    IncorrectlyFormattedDatabaseException
    {
        my_database = the_database;
        if (!my_database.canRead())    //checks whether the default file exists. If not, user is 
                                        //given the option to pick one himself.
        {
            JFileChooser fc = new JFileChooser();
            fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
            while (fc.showOpenDialog(null) != JFileChooser.APPROVE_OPTION)
            {
                
            }
            my_database = fc.getSelectedFile();
        }
        final Scanner parser = new Scanner(my_database);
        the_database = null;
        String cur_match = "";
        cur_match = parseHelper(parser, cur_match);     //FOUND UP IN THE STATIC FIELDS.
        if (!cur_match.equalsIgnoreCase(L_WICKET + MASTER_COLLECTION_TEXT + R_WICKET))
        {
            System.out.println("BAD INPUT FILE, EXPECTED <MASTERCOLLECTION>, GOT: " + cur_match);
            throw new IncorrectlyFormattedDatabaseException();
        }
        parser.useDelimiter("(</){1}[^>]*>{1}");

        while (!cur_match.matches("[^<>]*<{1}/{1}(?i)(" + MASTER_COLLECTION_TEXT +
                    "){1}>{1}"))
        {
            if (cur_match.equalsIgnoreCase(L_WICKET + USER_TEXT + R_WICKET))
            {
//                System.out.println("found user tag: " + cur_match);
                String username = null;
                UserType usertype = null;
                String userpass = null;
                while (!cur_match.matches("[^<>]*<{1}/{1}(?i)(" + USER_TEXT +
                            "){1}>{1}"))
                {
//                    System.out.println("Still in user tag: " + cur_match);
                    cur_match = parseHelper(parser, cur_match);
                    if(cur_match.equalsIgnoreCase(L_WICKET + USER_NAME_TEXT + R_WICKET))
                    {
                        username = parser.next().trim();
//                        System.out.println("In username match, parser.next returned: " +
//                        username);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET + USER_TYPE_TEXT + R_WICKET))
                    {
                        usertype = UserType.getType(parser.next().trim());
//                        System.out.println("In usertype match, parser.next returned: " +
//                        usertype);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET + USER_PASS_TEXT + R_WICKET))
                    {
                        userpass = parser.next().trim();
//                        System.out.println("In usertype match, parser.next returned: " +
//                        userpass);
                        cur_match = parseHelper(parser, cur_match);
                        
                    }
                    
                }
                my_master_collection.addUser(username, usertype, userpass);
//                System.out.println("end of user tag: " + cur_match);
            } else if (cur_match.equalsIgnoreCase(L_WICKET + AUCTION_TEXT + R_WICKET))
            {
//                System.out.println("found Auction tag: " + cur_match);
                
                Integer auctid = null;
                String auctnpo = null;
                String auctcontact = null;
                Long auctcontactnumber = null;
                String auctintake = null;
                Calendar auctdate = null;
                Integer auctduration = null;
                String auctcomments = null;
                List<Item> auctitems = new ArrayList<Item>();
                while (!cur_match.matches("[^<>]*<{1}/{1}(?i)(" +
                            AUCTION_TEXT + "){1}>{1}"))
                {
//                    System.out.println("no end of auction tag: " +
//                cur_match);
                    if(cur_match.equalsIgnoreCase(L_WICKET +
                                AUCTION_ID_TEXT + R_WICKET))
                    {
                        auctid = Integer.valueOf(parser.next().trim());
//                        System.out.println("In auctid match, " +
//                        "parser.next returned: " +
//                        auctid);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET +
                                AUCTION_NPO_TEXT + R_WICKET))
                    {
                        auctnpo = parser.next().trim();
//                        System.out.println("In auctnpo match, " +
//                        "parser.next returned: " +
//                        auctnpo);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET +
                                AUCTION_CONTACT_PERSON_TEXT + R_WICKET))
                    {
                        auctcontact = parser.next().trim();
//                        System.out.println("In contact match, " +
//                        "parser.next returned: " +
//                        auctcontact);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET +
                                AUCTION_CONTACT_PERSON_NUMBER_TEXT + R_WICKET))
                    {
                        auctcontactnumber = Long.valueOf(parser.next().trim());
//                        System.out.println("In contactnumber match, " +
//                        "parser.next returned: " + auctcontactnumber);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET + AUCTION_DATE_TEXT + R_WICKET))
                    {
                        String to_scan = parser.next().trim();
                        Scanner temp_scanner = new Scanner(to_scan);
                        auctdate = Calendar.getInstance();
                        auctdate.set(Integer.valueOf(temp_scanner.next().trim()).intValue(),
                                    Integer.valueOf(temp_scanner.next().trim()).intValue(), 
                                    Integer.valueOf(temp_scanner.next().trim()).intValue(),
                                    Integer.valueOf(temp_scanner.next().trim()).intValue(), 
                                    Integer.valueOf(temp_scanner.next().trim()).intValue());
//                        System.out.println("In date match, " +
//                                    "parser.next returned: " + auctdate);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET +
                                AUCTION_DURATION_TEXT + R_WICKET))
                    {
                        auctduration = Integer.valueOf(parser.next().trim());
//                        System.out.println("In duration match, " +
//                        "parser.next returned: " + auctduration);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET +
                                AUCTION_INTAKE_PERSON_TEXT + R_WICKET))
                    {
                        auctintake = parser.next().trim();
//                        System.out.println("In intake person match, " + 
//                        "parser.next returned: " + auctintake);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if(cur_match.equalsIgnoreCase(L_WICKET + AUCTION_COMMENT_TEXT +
                                R_WICKET))
                    {
                        auctcomments = parser.next().trim();
//                        System.out.println("In comments match, parser.next returned: " +
//                        auctcomments);
                        cur_match = parseHelper(parser, cur_match);
                        
                    } else if (cur_match.equalsIgnoreCase(L_WICKET + ITEM_TEXT + R_WICKET))
                    {                       //ITEM CASE
//                        System.out.println("found Item tag: " + cur_match);
                        
                        Integer id = null;
                        String name = null; 
                        Integer quantity = null;
                        Integer minbid = null;
                        String donor = null;
                        Integer size = null;
                        String storage = null;
                        String comment = null;
                        Map<String, Integer> bidtable = new HashMap<String, Integer>();
                        Condition itemcondition = null;
                        while (!cur_match.matches("[^<>]*<{1}/{1}(?i)(" + ITEM_TEXT + "){1}>{1}"))
                        {
//                            System.out.println("no end of item tag: " + cur_match);
                            cur_match = parseHelper(parser, cur_match);
                            if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_ID_TEXT + R_WICKET))
                            {
                                id = Integer.valueOf(parser.next().trim());
//                                System.out.println("In ITEMID match, parser.next returned: " + id);
                                cur_match = parseHelper(parser, cur_match);
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_NAME_TEXT + R_WICKET))
                            {
                                name = parser.next().trim();
//                                System.out.println("Name, returned: " + name);
                                cur_match = parseHelper(parser, cur_match);
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_QUANTITY_TEXT + R_WICKET))
                            {
                                quantity = Integer.valueOf(parser.next().trim());
//                                System.out.println("Quantity, returned: " + quantity);
                                cur_match = parseHelper(parser, cur_match);
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_MIN_BID_TEXT + R_WICKET))
                            {
                                minbid = Integer.valueOf(parser.next().trim());
//                                System.out.println("Min bid, returned: " + minbid);
                                cur_match = parseHelper(parser, cur_match);
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_DONOR_TEXT + R_WICKET))
                            {
                                donor = parser.next().trim();
//                                System.out.println("Donor, returned: " + donor);
                                cur_match = parseHelper(parser, cur_match);
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_SIZE_TEXT + R_WICKET))
                            {
                                size = Integer.valueOf(parser.next().trim());
//                                System.out.println("Item size, returned: " + size);
                                cur_match = parseHelper(parser, cur_match);
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_STORAGE_TEXT + R_WICKET))
                            {
                                storage = parser.next().trim();
//                                System.out.println("Storage, returned: " + storage);
                                cur_match = parseHelper(parser, cur_match);
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_CONDITION_TEXT + R_WICKET))
                            {
                                itemcondition = Condition.conditionFromString(parser.next().trim());
//                                System.out.println("Condition, returned: " + itemcondition.toString());
                                cur_match = parseHelper(parser, cur_match);
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_BIDS_TEXT + R_WICKET))
                            {
                                while (!cur_match.matches("[^<>]*<{1}/{1}(?i)(" + ITEM_BIDS_TEXT + "){1}>{1}"))
                                {   
                                    if (cur_match.equalsIgnoreCase(L_WICKET + BID_TEXT + R_WICKET))
                                    {
                                        String to_scan = parser.next().trim();
                                        Scanner temp_scanner = new Scanner(to_scan);

                                        bidtable.put(temp_scanner.next().trim(), 
                                                    Integer.valueOf(temp_scanner.next().trim()));
//                                        System.out.println("In bids looper, temp_scanner of bids: " +
//                                                    to_scan);
                                    }
                                    cur_match = parseHelper(parser, cur_match);
                                }
                                
                            } else if(cur_match.equalsIgnoreCase(L_WICKET + ITEM_COMMENT_TEXT + R_WICKET))
                            {
                                comment = parser.next().trim();
//                                System.out.println("Comment, returned: " + comment);
                                cur_match = parseHelper(parser, cur_match);
                                
                            }
                        }
                       try 
                       {
                           Item temp_item = new Item(id, name, 
                                       quantity, minbid, donor,
                                       size, storage, itemcondition, comment);
                           for (Entry<String, Integer> e : bidtable.entrySet())
                           {
                               temp_item.addBid(e.getKey(), e.getValue().intValue());
                           }
                           auctitems.add(temp_item);
                       } catch (IllegalArgumentException e)
                       {
  System.err.println("Bad input, item could not be constructed" +
  "with the parameters in the given format. \nItem name: " + name);
 
                       } catch (NullPointerException e)
                       {
                           System.out.println("Bad input, empty item tag! >:(");
                       }
                    } 

                    cur_match = parseHelper(parser, cur_match);
                    
                } 

                my_master_collection.addAuction(auctid.intValue(), auctnpo, auctcontact, auctcontactnumber.longValue(), auctintake, 
                            auctdate, auctduration.intValue(), auctcomments);

                for (Item i : auctitems)
                {
                    my_master_collection.addAuctionItem(auctid, i);
                }
                
//                System.out.println("end of user tag: " + cur_match);
            } else if (cur_match.equalsIgnoreCase(L_WICKET + STATIC_TEXT + R_WICKET))
            {
                while (!cur_match.matches("[^<>]*<{1}/{1}(?i)(" + STATIC_TEXT + "){1}>{1}"))
                {
                    cur_match = parseHelper(parser, cur_match);
                    if (cur_match.equalsIgnoreCase(L_WICKET + STATIC_NEXT_AUCTION_TEXT + R_WICKET))
                    {
                        Auction.setNextID(Integer.valueOf(parser.next().trim()).intValue());
                        
                    } else if (cur_match.equalsIgnoreCase(L_WICKET + STATIC_NEXT_ITEM_TEXT + R_WICKET))
                    {
                        System.out.println("setting nextItemID");
                        Item.setNextItemID(Integer.valueOf(parser.next().trim()).intValue());
                    }
                }
                
            }
            cur_match = parseHelper(parser, cur_match);
        }
    
    
    }
    
    
    /**
     * Writes the data to the file.
     * 
     * @requires the_file XML-written
     * @ensures Proper save for the database
     */
    public void writeData(File the_file)
    {
        boolean working = true;
        while (!working || !the_file.canWrite())
        {
            try
            {
                the_file.createNewFile();
            } catch (IOException e)
            {
                working = false;
                JFileChooser fc = new JFileChooser();
                fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
                while (fc.showSaveDialog(null) != JFileChooser.APPROVE_OPTION)
                {
                    the_file = fc.getSelectedFile();
                }
            }

            
        }
        
        try
        {
            FileWriter fstream = new FileWriter(the_file);
            BufferedWriter out = new BufferedWriter(fstream);
            StringBuilder sb = new StringBuilder();

            sb.append(L_WICKET + MASTER_COLLECTION_TEXT + R_WICKET + NEWLINE);

            sb.append(TABCHAR + L_WICKET + STATIC_TEXT + R_WICKET + NEWLINE);
//Writes static next auction and next item integers.
            sb.append(TABCHAR + TABCHAR + L_WICKET + STATIC_NEXT_AUCTION_TEXT + R_WICKET);
            sb.append(Auction.next_auction_id);
            sb.append(L_SLASH_WICKET + STATIC_TEXT + R_WICKET + NEWLINE);
            sb.append(TABCHAR + TABCHAR + L_WICKET + STATIC_NEXT_ITEM_TEXT + R_WICKET);
            sb.append(Item.next_item_id);
            sb.append(L_SLASH_WICKET + STATIC_NEXT_ITEM_TEXT + R_WICKET + NEWLINE);
//Finished writing statics.
            sb.append(TABCHAR + L_SLASH_WICKET + STATIC_TEXT + R_WICKET + NEWLINE + NEWLINE);
            
            for (User u : my_master_collection.getUsers())
            {
                sb.append(TABCHAR + L_WICKET + USER_TEXT + R_WICKET + NEWLINE);
                
                sb.append(TABCHAR + TABCHAR + L_WICKET + USER_NAME_TEXT + R_WICKET);
                sb.append(u.getName());
                sb.append(L_SLASH_WICKET + USER_NAME_TEXT + R_WICKET + NEWLINE);

                sb.append(TABCHAR + TABCHAR + L_WICKET + USER_TYPE_TEXT + R_WICKET);
                sb.append(u.getType().toString());
                sb.append(L_SLASH_WICKET + USER_TYPE_TEXT + R_WICKET + NEWLINE);
                
                sb.append(TABCHAR + TABCHAR + L_WICKET + USER_PASS_TEXT + R_WICKET);
                sb.append(u.getPassword());
                sb.append(L_SLASH_WICKET + USER_PASS_TEXT + R_WICKET + NEWLINE);

                sb.append(TABCHAR + L_SLASH_WICKET + USER_TEXT + R_WICKET + NEWLINE + NEWLINE);
            }
            
            for (Auction a : my_master_collection.getAuctions())
            {
//Writes Auction info
                sb.append(TABCHAR + L_WICKET + AUCTION_TEXT + R_WICKET + NEWLINE);
                
                sb.append(TABCHAR + TABCHAR + L_WICKET + AUCTION_ID_TEXT + R_WICKET);
                sb.append(a.getAuctionID());
                sb.append(L_SLASH_WICKET + AUCTION_ID_TEXT + R_WICKET + NEWLINE);

                sb.append(TABCHAR + TABCHAR + L_WICKET + AUCTION_NPO_TEXT + R_WICKET);
                sb.append(a.getNPO());
                sb.append(L_SLASH_WICKET + AUCTION_NPO_TEXT + R_WICKET + NEWLINE);
                
                if (a.getContactPerson() != null || !"".equals(a.getContactPerson()))
                {
                    sb.append(TABCHAR + TABCHAR + L_WICKET + AUCTION_CONTACT_PERSON_TEXT +
                        R_WICKET);
                    sb.append(a.getContactPerson());
                    sb.append(L_SLASH_WICKET + AUCTION_CONTACT_PERSON_TEXT + R_WICKET +
                        NEWLINE);
                }
                
                
                sb.append(TABCHAR + TABCHAR + L_WICKET + AUCTION_CONTACT_PERSON_NUMBER_TEXT + R_WICKET);
                sb.append(a.getContactNumber());
                sb.append(L_SLASH_WICKET + AUCTION_CONTACT_PERSON_NUMBER_TEXT + R_WICKET + NEWLINE);

                if (a.getACIntake() != null || !"".equals(a.getACIntake()))
                {
                    sb.append(TABCHAR + TABCHAR + L_WICKET + AUCTION_INTAKE_PERSON_TEXT +
                        R_WICKET);
                    sb.append(a.getACIntake());
                    sb.append(L_SLASH_WICKET + AUCTION_INTAKE_PERSON_TEXT + R_WICKET + NEWLINE);
                }
                
                sb.append(TABCHAR + TABCHAR + L_WICKET + AUCTION_DATE_TEXT + R_WICKET);
                Calendar temp_cal = a.getDate();
                sb.append(temp_cal.get(Calendar.YEAR) + SPACE + temp_cal.get(Calendar.MONTH) + SPACE +
                            temp_cal.get(Calendar.DAY_OF_MONTH) + SPACE + temp_cal.get(Calendar.HOUR_OF_DAY) +
                            SPACE + temp_cal.get(Calendar.MINUTE));
                sb.append(L_SLASH_WICKET + AUCTION_DATE_TEXT + R_WICKET + NEWLINE);
                
                sb.append(TABCHAR + TABCHAR + L_WICKET + AUCTION_DURATION_TEXT + R_WICKET);
                sb.append(a.getDuration());
                sb.append(L_SLASH_WICKET + AUCTION_DURATION_TEXT + R_WICKET + NEWLINE);
                
                if (a.getComments() != null || !"".equals(a.getComments()))
                {
                    sb.append(TABCHAR + TABCHAR + L_WICKET + AUCTION_COMMENT_TEXT + R_WICKET);
                    sb.append(a.getComments());
                    sb.append(L_SLASH_WICKET + AUCTION_COMMENT_TEXT + R_WICKET + NEWLINE);
                }
                List<Item> temp_items = a.getItems();
                for (Item item : temp_items)
                {
//Writes Item info
                    sb.append(NEWLINE + TABCHAR + TABCHAR + L_WICKET + ITEM_TEXT + R_WICKET + NEWLINE);
                    
                    sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_ID_TEXT + R_WICKET);
                    sb.append(item.getItemID());
                    sb.append(L_SLASH_WICKET + ITEM_ID_TEXT + R_WICKET + NEWLINE);
                    
                    sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_NAME_TEXT + R_WICKET);
                    sb.append(item.getItemName());
                    sb.append(L_SLASH_WICKET + ITEM_NAME_TEXT + R_WICKET + NEWLINE);
                    
                    sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_QUANTITY_TEXT + R_WICKET);
                    sb.append(item.getQuantity());
                    sb.append(L_SLASH_WICKET + ITEM_QUANTITY_TEXT + R_WICKET + NEWLINE);
                    
                    sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_MIN_BID_TEXT + R_WICKET);
                    sb.append(item.getMinStartingBid());
                    sb.append(L_SLASH_WICKET + ITEM_MIN_BID_TEXT + R_WICKET + NEWLINE);
                    
                    if (item.getDonor() != null || !"".equals(item.getDonor()))
                    {
                        sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_DONOR_TEXT +
                            R_WICKET);
                        sb.append(item.getDonor());
                        sb.append(L_SLASH_WICKET + ITEM_DONOR_TEXT + R_WICKET + NEWLINE);
                    }
                    
                    sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_SIZE_TEXT + R_WICKET);
                    sb.append(item.getSize());
                    sb.append(L_SLASH_WICKET + ITEM_SIZE_TEXT + R_WICKET + NEWLINE);
                    
                    if (item.getStorage() != null || !"".equals(item.getStorage()))
                    {
                        sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_STORAGE_TEXT +
                            R_WICKET);
                        sb.append(item.getStorage());
                        sb.append(L_SLASH_WICKET + ITEM_STORAGE_TEXT + R_WICKET + NEWLINE);
                    }
                    
                    sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_CONDITION_TEXT + R_WICKET);
                    sb.append(item.getCondition().toString());
                    sb.append(L_SLASH_WICKET + ITEM_CONDITION_TEXT + R_WICKET + NEWLINE);
                    
                    if (item.getComment() != null || !"".equals(item.getComment()))
                    {
                        sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_COMMENT_TEXT +
                            R_WICKET);
                        sb.append(item.getComment());
                        sb.append(L_SLASH_WICKET + ITEM_COMMENT_TEXT + R_WICKET + NEWLINE);
                    }
                    
                    sb.append(TABCHAR + TABCHAR + TABCHAR + L_WICKET + ITEM_BIDS_TEXT + R_WICKET + NEWLINE);
                    Map<String, Integer> temp_bids = item.getBids();
                    Set<String> temp_keys = temp_bids.keySet();
                    for (String s : temp_keys)
                    {
//Writes bids                            
                        sb.append(TABCHAR + TABCHAR + TABCHAR + TABCHAR + L_WICKET + BID_TEXT + R_WICKET);
                        sb.append(s + SPACE + temp_bids.get(s));
                        sb.append(L_SLASH_WICKET + BID_TEXT + R_WICKET + NEWLINE);
                    }
                    
                    sb.append(NEWLINE + TABCHAR + TABCHAR + TABCHAR + L_SLASH_WICKET + ITEM_BIDS_TEXT +
                                R_WICKET + NEWLINE);
                    
                    sb.append(TABCHAR + TABCHAR + L_SLASH_WICKET + ITEM_TEXT + R_WICKET + NEWLINE);
                }

                sb.append(TABCHAR + L_SLASH_WICKET + AUCTION_TEXT + R_WICKET + NEWLINE + NEWLINE);
            }

            sb.append(L_SLASH_WICKET + MASTER_COLLECTION_TEXT + R_WICKET + NEWLINE);
                
            out.write(sb.toString());
            out.close();
        } catch (Exception e){
                System.err.println("Error: " + e.getMessage());
        }
        

    }

    /**
     * Writes the data out to persistant storage.
     * 
     * @author Travis Drake
     */
    public void finalizeData()
    {
        writeData(my_database);
    }
    
}
